#ifndef METAFILE_H_
#define METAFILE_H_

#include "status.hxx"
#include <string>
#include <map>

class RandomAccessFile;


struct SegmentKey {

    SegmentKey() {};
    SegmentKey(const std::string& store, const std::string& partition, uint16_t segID) 
    : m_store(store), 
      m_partition(partition),
      m_segID(segID) {
    
    }

    std::string m_store;
    std::string m_partition;
    uint16_t m_segID;

    std::string serialize();
    void deserialize(const std::string& data);
    std::string toString();
};

class SegmentKeyComparator {

public:

    bool operator()(const SegmentKey& left, const SegmentKey& right) {
    
        return (left.m_store < right.m_store) || 
              (left.m_store == right.m_store && left.m_partition < right.m_partition) ||
              (left.m_store == right.m_store && left.m_partition ==
               right.m_partition && left.m_segID < right.m_segID);
    }
};

struct SegmentInfo {

    SegmentInfo():m_actualDataSize(0), m_isAlive(1){}

    int32_t m_actualDataSize;
    uint32_t m_isAlive;

    std::string serialize();
    void deserialize(const std::string& data);
    std::string toString();
};

class MetaFileHeader {

private:
    uint64_t m_version;
    uint64_t m_magic;

    static uint64_t MAGIC;

public:
    static uint32_t METAFILE_HEADER_LENGTH;

public:
    MetaFileHeader();

    std::string serialize();
    void deserialize(const std::string& data);

    bool checkHeader();

    std::string toString();

};

class MetaFile {

private:
    std::string m_envPath;
    MetaFileHeader m_header;
    RandomAccessFile* m_raf;

    //mutex is used to protect the concurrent access of m_seginfoMap 
    pthread_mutex_t m_mutex; 
    std::map<SegmentKey, SegmentInfo, SegmentKeyComparator> m_seginfoMap;
    std::map<uint32_t, uint32_t> m_logSynced;

    static MetaFile* m_instance;

public:
    MetaFile(const std::string& envPath);
    ~MetaFile();

    DbStatus init();
    
    DbStatus flush();

    uint32_t getDataSize(const std::string& store,
                         const std::string& partition,
                         uint16_t segID);

    void setDataSize(const std::string& store, 
                     const std::string& partition,
                     uint16_t segID, 
                     uint32_t size);

    uint32_t changeDataSize(const std::string& store, 
                            const std::string& partition,
                            uint16_t segID, 
                            uint32_t size);

    void removeSeg(const std::string& store,
                   const std::string& partition,
                   uint16_t segID);

    bool isAlive(const std::string& store, 
                 const std::string& partition,
                 uint16_t segID);

    void setAlive(const std::string& store, 
                  const std::string& partition,
                  uint16_t segID,
                  bool alive);

    void setLogSync(uint32_t logID, uint32_t synced);
    void removeLog(uint32_t logID);
    bool isLogSynced(uint32_t logID);
    
    std::string toString();

    static MetaFile* getInstance(const std::string& envPath = "");

private:
    DbStatus loadHeader();
    DbStatus saveHeader();

    DbStatus loadData();
    DbStatus saveData();

    std::string segSerialize();
    void segDeserialize(const std::string& data);
    std::string logSerialize();
    void logDeserialize(const std::string& data);
};

#endif
